package com.github.m6d21.sso.support.annotation;

import com.alibaba.fastjson.JSON;
import com.github.m6d21.sso.core.exception.BusinessException;
import com.github.m6d21.sso.support.enums.StatusCode;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
 *<p>
 *
 *</p>
 *
 * @author liuyang
 * @date 2018/12/18 Tue 21:57:00
 * @since 1.0.0
 */
public class ArrayArgumentResolver implements HandlerMethodArgumentResolver {

    private static final Logger LOG = LoggerFactory.getLogger(ArrayArgumentResolver.class);

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        //参数上包含DateParam注解，并且参数类型是日期
        return parameter.hasParameterAnnotation(RequestArrayParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

        HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);

        RequestArrayParam arrayParam = parameter.getParameterAnnotation(RequestArrayParam.class);
        Object array = null;
        Class type = arrayParam.type();
        String parameterName = getParameterName(parameter);
        String value = httpServletRequest.getParameter(parameterName);
        if (StringUtils.isNotEmpty(value)) {
            //去除前后空格
            value = value.trim();
            if (value.startsWith("[") && value.endsWith("]")) {
                //去除前后的中括号
                value = value.substring(1, value.length() - 1);
            }
            Iterator<String> elements = Splitter.on(",").trimResults().omitEmptyStrings().split(value).iterator();
            try {
                List<String> list = Lists.newArrayList(elements);
                array = stringToObjectArray(list, type);
            } catch (Exception e) {
                LOG.error("{}无法转换为数组, error:{}", value, e);
                throw new BusinessException(StatusCode.FAIL.getCode(), "不支持改参数类型：" + value);
            }
        } else {
            resolveRequired(parameter);
        }
        return array;
    }

    private String getParameterName(MethodParameter parameter) {
        RequestArrayParam parameterAnnotation = parameter.getParameterAnnotation(RequestArrayParam.class);
        String parameterName = parameterAnnotation.value();
        if (StringUtils.isEmpty(parameterName)) {
            parameterName = parameter.getParameterName();
        }
        return parameterName;
    }

    private void resolveRequired(MethodParameter parameter) throws Exception {
        RequestArrayParam parameterAnnotation = parameter.getParameterAnnotation(RequestArrayParam.class);
        if (parameterAnnotation.required()) {
            throw new MissingServletRequestParameterException(getParameterName(parameter), "String");
        }
    }

    private Object toBaseType(String value, Class type) {
        if (Byte.class.equals(type)) {
            return Byte.parseByte(value);
        } else if (Integer.class.equals(type)) {
            return Integer.parseInt(value);
        } else if (Boolean.class.equals(type)) {
            return Boolean.parseBoolean(value);
        } else if (Long.class.equals(type)) {
            return Long.parseLong(value);
        } else if (Double.class.equals(type)) {
            return Double.parseDouble(value);
        }
        return null;
    }

    private Boolean isBaseType(Class type) {
        if (Byte.class.equals(type)) {
            return true;
        } else if (Integer.class.equals(type)) {
            return true;
        } else if (Boolean.class.equals(type)) {
            return true;
        } else if (Long.class.equals(type)) {
            return true;
        } else if (Double.class.equals(type)) {
            return true;
        }
        return false;
    }

    private Object stringToObjectArray(List<String> list, Class type) {
        List<Object> objectList = Lists.newArrayList();
        if (isBaseType(type)) {
            list.forEach(s -> objectList.add(toBaseType(s, type)));
        } else {
            list.forEach(s -> objectList.add(JSON.parseObject(s, type)));
        }
        int len = objectList.size();
        Object array= Array.newInstance(type, len);
        for(int i=0;i<len;i++){
            Array.set(array,i,objectList.get(i));
        }
        return array;
    }
}
